#include <buola/scene/ctransform.h>
#include <buola/scene/cobject.h>
#include <buola/scene/crendercontext.h>

#include <buola/gl.h>
#include <buola/algorithm/container.h>
#include <buola/io/curi.h>

#include "import/cvrmlimporter.h"
#include "import/curdfimporter.h"

namespace buola { namespace scene {

CTransform::CTransform()
    :   mParent(nullptr)
    ,   mLocalTransformValid(false)
{
}

CTransform::CTransform(const std::string &pName)
    :   mParent(nullptr)
    ,   mName(pName)
    ,   mLocalTransformValid(false)
{
}

CTransform::~CTransform()
{
    msg_info() << "erasing transform!!!!\n";
    if(mParent)
        erase_all_equal(mParent->mChildren,this);
}

void CTransform::AddChild(PTransform pChild)
{
    if(pChild->mParent)
        erase_all_equal(pChild->mParent->mChildren,pChild);
    mChildren.push_back(pChild);
    pChild->mParent=this;
}

void CTransform::RemoveChild(PTransform pChild)
{
    assert(this==pChild->mParent);
    erase_all_equal(mChildren,pChild);
    pChild->mParent=nullptr;
}

void CTransform::AddObject(PObject pObject)
{
    mObjects.push_back(pObject);
}

void CTransform::RemoveObject(PObject pObject)
{
    erase_all_equal(mObjects,pObject);
}

void CTransform::RemoveAllObjects()
{
    mObjects.clear();
}

void CTransform::Update()
{
    if(!mLocalTransformValid)
        CalcLocalTransform();
    if(mParent)
        mWorldTransform=mParent->mWorldTransform*mLocalTransform;
    else
        mWorldTransform=mLocalTransform;

    for(int i=0;i<mChildren.size();i++)
        mChildren[i]->Update();
}

void CTransform::Render(CRenderContext &pContext)
{
    for(int i=0;i<mChildren.size();i++)
        mChildren[i]->Render(pContext);

    pContext.mModelMatrix=mWorldTransform;
    if(pContext.mTransformIndex)
        pContext.mTransforms.push_back(this);
    
    for(int i=0;i<mObjects.size();i++)
        mObjects[i]->Render(pContext);
}

void CTransform::CollectPolygons(std::vector<mat::CVec3d> &pPoints,std::vector<mat::CVec3d> &pNormals,std::vector<int> &pFaces)
{
    for(int i=0;i<mChildren.size();i++)
        mChildren[i]->CollectPolygons(pPoints,pNormals,pFaces);

    for(int i=0;i<mObjects.size();i++)
        mObjects[i]->CollectPolygons(pPoints,pNormals,pFaces);
}

bool CTransform::IsAncestorOf(const PTransform &pOther) const
{
    for(const PTransform &t : mChildren)
    {
        if(t==pOther||t->IsAncestorOf(pOther)) return true;
    }
    
    return false;
}

PTransform CTransform::Import(const io::CURI &pURI)
{
    std::string lExtension=pURI.Extension();

    if(lExtension=="vrml")
    {
        import::CVRMLImporter lVRMLImporter;
        return lVRMLImporter.Parse(pURI);
    }
    if(lExtension=="urdf")
    {
        import::CURDFImporter lURDFImporter;
        return lURDFImporter.Parse(pURI);
    }
    else
    {
        throw XArgument("extension not recognized in CTransform::Import");
    }
}

PTransform CTransform::FindDescendant(const std::string &pName)
{
    if(pName==mName)
        return this;
    
    for(const PTransform &c : mChildren)
    {
        if(PTransform lReturn=c->FindDescendant(pName))
            return lReturn;
    }
    
    return nullptr;
}

void CTransform::PrintTransformTree(int pDepth)
{
    msg_info() << std::string(pDepth*3,' ') << (mName.empty()?"---":mName) << "\n";
    for(const PTransform &c : mChildren)
    {
        c->PrintTransformTree(pDepth+1);
    }
}

/*namespace scene*/ } /*namespace buola*/ }
